/*
 * This file is part of FlashLogger, http://code.google.com/p/flashlogger
 *
 * By: Thom Porter
 * Copyright (c) 2009 Thom Porter, www.thomporter.com

 * Released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 */

(function()
{
	var runLogger = $FL('_FLC_RunLog').on();
	var id = "flashLoggerConsole";
	var consoleCreated = false;
	var draggingConsole = false;
	var mousePos = {};
	var activeBtn = 'all';
	var activeLog = '_FLCAllLogers';
	var instance_ids = ['_FLCAllLogers','_FLCMainLogger','_FLC_RunLog'];
	var initial_instance_list = '<a id="#_FLCAllLogers">(all)</a><a id="#_FLCMainLogger">(main)</a><a id="#_FLC_RunLog" style="display:none">(run log)</a>';

	// event listeners
	function addListener(target,type,callback,captures) {
	    if (target.addEventListener) {
	        target.addEventListener(type,callback,captures);
	    } else if (target.attachEvent) {
	        target.attachEvent('on'+type,callback,captures);
	    } else {
	    	target['on'+type] = callback;
	    }
	}

	// shortcut
	function el(id) {
		return document.getElementById(id);
	}

	function in_array(needle,haystack)
	{
		for (var i in haystack)
		{
			if (needle == haystack[i]) {
				return true;
			}
		}
		return false;
	}

	// get event target
	function eventTarget(e)
	{
		if (e.srcElement != null) return e.srcElement
		return e.target;
	}

	// stripped from jQuery, thanks John Resig - http://jquery.com
	function each(obj, fn) {
		if (!obj) { return; }

		var name, i = 0, length = obj.length;

		// object
		if (length === undefined) {
			for (name in obj) {
				try {
					if (fn.call(obj[name], name, obj[name]) === false) { break; }
				} catch (e) {}
			}

		// array
		} else {
			for (var value = obj[0];
				i < length && fn.call( value, i, value ) !== false; value = obj[++i]) {
			}
		}

		return obj;
	}

	function extend(o1, o2, sf)
	{
		if (typeof o2 != 'object') { return o1; }

		if (o1 && o2) {
			each(o2, function(k, v) {
				if (!sf || typeof v != 'function') {
					o1[k] = v;
				}
			});
		}

		return o1;
	}

	function appendHtml(o,html)
	{
		if (arguments.length>2) {
			mode = arguments[2];
		} else {
			mode = 'append';
		}
		if (document.all)
		{
			if (mode == 'append') {
				o.insertAdjacentHTML('beforeEnd', html);
			} else {
				o.insertAdjacentHTML('afterBegin', html);
			}
		}
		else if (document.createRange)
		{
			var range = document.createRange();
			if (mode == 'append') {
				range.setStartAfter(o.lastChild);
				var cFrag = range.createContextualFragment(html);
				o.appendChild(cFrag);
			} else {
				range.setStartBefore(o.firstChild);
				var cFrag = range.createContextualFragment(html);
				o.insertBefore(cFrag, o.firstChild);
			}
		}
		else if (document.layers)
		{
			var X = new Layer(window.innerWidth);
			X.document.open();
			X.document.write(html);
			X.document.close();
			X.top = document.height;
			document.height += X.document.height;
			X.visibility = 'show';
		}
	}

	// function to actually create the main console and attach it to the DOM
	function create() {
		if (!document.body){
			window.setTimeout(create, 250)
			return;
		}
		consoleCreated = true;
		var html = '<div id="' + id + '" class="flashLoggerConsole">';
		html += '<h1 id="flcTitle"><span id="flcOpenClose">+/-</span>Flash Logger Console</h1>';
		html += '<div id="flcContentPanel">';
		html += '<div id="flcInstanceListPanel"><h2>Loggers</h2><div id="flcInstanceList">' + initial_instance_list + '</div></div>';
		html += '<div id="flcButtons"><a href="#" id="flcButton_all" class="flcActiveButton">All</a><a href="#" id="flcButton_log">Log</a><a href="#" id="flcButton_info">Info</a><a href="#" id="flcButton_warn">Warn</a><a href="#" id="flcButton_error">Error</a><a href="#" id="flcClearButton">Clear</a></div>';
		html += '<div id="flcHistoryPanel"><div></div></div>';
		html += '<div id="flcInspectorPanel"><div></div></div>';
		html += '<form id="flcCommandPanel">';
		html += 'Run: <input type="text" id="flcRunCommand" name="flcRunCommand" value="" /> <a href="#" id="flcRunCommandBtn" name="flcRunCommandBtn">Run</a>';
		html += '</form>';
		html += '</div>';
		html += '</div>';

		appendHtml(document.body,html);

		addListener(el("flcCommandPanel"), 'submit', consoleRunCommandClick);
		addListener(el("flcRunCommandBtn"), 'click', consoleRunCommandClick);
		addListener(el("flcTitle"), 'mousedown', flashLoggerDragStart);
		addListener(el("flcOpenClose"), 'mousedown', openCloseConsole);
		var e = el(id);
		e.style.top = '0px';
		e.style.left = document.body.clientWidth - 550 + 'px';

		// bind the console buttons
		addListener(el("flcClearButton"), 'click', clearConsole);
		addListener(el("flcButton_all"), 'click', consoleButtonClick);
		addListener(el("flcButton_log"), 'click', consoleButtonClick);
		addListener(el("flcButton_info"), 'click', consoleButtonClick);
		addListener(el("flcButton_warn"), 'click', consoleButtonClick);
		addListener(el("flcButton_error"), 'click', consoleButtonClick);
		// open the console if it's supposed to be opened...
		if ($FL().isConsoleOpened()) {
			el('flashLoggerConsole').style.display = 'block'
			centerMe(true);
		}
		addListener(document,'mousemove', flashLoggerMouseMove);
		addListener(document,'mouseup', flashLoggerDragStop);
	}
	if ($FL().isConsoleOpened()) create();

	function clearConsole() {
		el('flcHistoryPanel').style.display = 'block';
		el('flcInspectorPanel').style.display = 'none';
		$FLC.clear();
		return false;
	}

	function consoleRunCommandClick(e)
	{
		el('flcHistoryPanel').style.display = 'block';
		el('flcInspectorPanel').style.display = 'none';
		try {
			var cmd = el('flcRunCommand').value;
			var o = eval(cmd);
			if (o != null && o != undefined && !o.consoleIncluded) runLogger.log(o);
		} catch(e) {
			runLogger.error('eval failed!');
		}
		if (e.preventDefault) e.preventDefault();
		else return false;
	}

	function consoleButtonClick(e)
	{
		e = window.event || e;
		var tar = eventTarget(e);
		var t = tar.id.substr(10);
		el('flcButton_' + activeBtn).className = '';
		activeBtn = t;
		tar.className = 'flcActiveButton';
		updateHistory();
		if (e.preventDefault) e.preventDefault();
		else return false;
	}

	function instanceListClick(e)
	{
		e = window.event || e;
		var tar = eventTarget(e);
		if (tar.id != 'flcInstanceList') {
			var t = tar.id.substr(1);
			activeLog = t;
			updateHistory();
		}
		if (e.preventDefault) e.preventDefault();
		else return false;
	}

	function updateHistory()
	{
		el('flcHistoryPanel').style.display = 'block';
		el('flcInspectorPanel').style.display = 'none';
		var divs = el('flcHistoryPanel').childNodes;
		for (var i=0;i<divs.length;i++) {
			if (activeBtn == 'all') {
				divs[i].style.display = 'block';
			} else {
				if (divs[i].className.indexOf('flcItem_' + activeBtn)==-1) {
					divs[i].style.display = 'none';
				} else {
					divs[i].style.display = 'block';
				}
			}
			if (activeLog != '_FLCAllLogers' && divs[i].className.indexOf('flc_' + activeLog)==-1)
			{
				divs[i].style.display = 'none';

			}
		}
	}



	function flashLoggerDragStart(e)
	{
		e = window.event || e;
		updateMousePos(e);
		draggingConsole = true;
	}

	function flashLoggerDragStop(e) { draggingConsole = false; }
	function flashLoggerMouseMove(e) {
		if (!draggingConsole) {
			return;
		}
		e = window.event || e;
		var c = el(id);
		var t = parseInt(c.style.top) + (e.clientY-mousePos.y);
		if (t<0) t = 0;
		var l = parseInt(c.style.left) + (e.clientX-mousePos.x);
		if (l<0) l = 0;

		c.style.top = t + 'px';
		c.style.left = l + 'px';
		updateMousePos(e);
	}

	// handles opening and closing the console
	function openCloseConsole()
	{
		if (!consoleCreated)
		{
			create();
		}
		var e = el(id);
		if (arguments.length && arguments[0]==1) {
			e.style.height = '350px';
			el('flcContentPanel').style.display = 'block';
			$FL().consoleOpened(true);
		} else if (arguments.length && arguments[0]==-1) {
			e.style.height = '17px';
			el('flcContentPanel').style.display = 'none';
			$FL().consoleOpened(false);
		} else {
			if ($FL().isConsoleOpened()) {
				e.style.height = '17px';
				el('flcContentPanel').style.display = 'none';
			} else {
				e.style.height = '350px';
				el('flcContentPanel').style.display = 'block';
			}
			$FL().consoleOpened(!$FL().isConsoleOpened());
		}
	}


	function centerMe(doHoriz)
	{
		var dw = 0, dh = 0, sx = 0, sy = 0;
		if( typeof( window.innerWidth ) == 'number' ) {
			//Non-IE
			dw = window.innerWidth;
			dh = window.innerHeight;
		} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
			//IE 6+ in 'standards compliant mode'
			dw = document.documentElement.clientWidth;
			dh = document.documentElement.clientHeight;
		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
			//IE 4 compatible
			dw = document.body.clientWidth;
			dh = document.body.clientHeight;
		}

		if( typeof( window.pageYOffset ) == 'number' ) {
			//Netscape compliant
			sy = window.pageYOffset;
			sx = window.pageXOffset;
		} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			//DOM compliant
			sy = document.body.scrollTop;
			sx = document.body.scrollLeft;
		} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			//IE6 standards compliant mode
			sy = document.documentElement.scrollTop;
			sx = document.documentElement.scrollLeft;
		}

		var tar = el('flashLoggerConsole');
		if (doHoriz) tar.style.left = (dw/2-300+sx) + 'px'
		tar.style.top = (dh/2-175+sy) + 'px'


	}


	function updateMousePos(e) {
		mousePos.x = e.clientX;
		mousePos.y = e.clientY;
	}


	function itemRow(item)
	{
		var type = typeof item;
		var listen_to = false;
		var s;
		if (type == "object" && item.push) {
			var objId = $FLC.addObject(item);
			listen_to = 'flcObject_' + objId;
			s = '<a href="#" class="flcArrayKey" id="' + listen_to + '">Array</a>';
			var obj_items = [];
			var more_count = 0;
			each(item,function(k,v){
				if (obj_items.length > 4) { more_count++; return; }
				var ss = '';
				var vt = typeof v;
				switch (vt) {
					case 'function':
						ss += vt;
						break;
					case 'object' :
						if (v.push != null) {
							ss += 'array'
						} else {
							ss += 'object'
						}
						break;
					case 'string' :
						ss += '"' + v + '"';
						break;
					case 'boolean': case 'number':
						ss += v;
						break;
					default:
						ss += 'unknown type (' + vt + ')';
				}
				obj_items.push(ss);
			});
			s += ' - ([' + obj_items.join(', ');
			if (more_count > 0) s += ', (' + more_count + 'more)';
			s += '])';
		} else if (type == "object") {
			var objId = $FLC.addObject(item);
			listen_to = 'flcObject_' + objId;
			s = '<a href="#" class="flcObjectKey" id="' + listen_to + '">Object</a>';
			var obj_items = [];
			var more_count = 0;
			each(item,function(k,v){
				if (obj_items.length > 4) { more_count++; return; }
				var ss = k + ':';
				var vt = typeof v;
				switch (vt) {
					case 'function':
						ss += vt;
						break;
					case 'object' :
						if (v != null && v.push != null) {
							ss += 'array'
						} else {
							ss += 'object'
						}
						break;
					case 'string' :
						ss += '"' + v + '"';
						break;
					case 'boolean': case 'number':
						ss += v;
						break;
					default:
						ss += 'unknown type (' + vt + ')';
				}
				obj_items.push(ss);
			});
			s += ' - ({' + obj_items.join(', ');
			if (more_count > 0) s += ', (' + more_count + 'more)';
			s += '})';
		} else {
			s = item;
		}
		return [s,listen_to]
	}

	function add_instance(id)
	{
		instance_ids.push(id);
		var html='<a href="#" id="#' + id + '">' + id + '</a>';
		appendHtml(el('flcInstanceList'), html);
		addListener(el('flcInstanceList'), 'click', instanceListClick);
	}


	function addRowToHistory(type,args)
	{
		var instance_id = args[0];
		if (!in_array(instance_id,instance_ids)) add_instance(instance_id);
		if (instance_id == '_FLC_RunLog') el('#_FLC_RunLog').style.display = 'block';

		var html = '<div class="flcItem_' + type + ' flc_' + instance_id + '"';
		if ( (activeBtn != 'all' && type != activeBtn) || (activeLog!='_FLCAllLogers' && instance_id!=activeLog)) {
			html += ' style="display:none;"';
		}
		html += '>';
		var listeners = [];
		if (args.length == 2) {
			var ir = itemRow(args[1]);
			if (ir[1]!=false) listeners.push(ir[1]);
			html += ir[0];
		}
		else {
			var ir = itemRow(args[1]);
			if (ir[1]!=false) listeners.push(ir[1]);
			html += ir[0] + ' ';
			var ir = itemRow(args[2]);
			if (ir[1]!=false) listeners.push(ir[1]);
			html += ir[0];
		}
		html += '</div>';
		var e = el('flcHistoryPanel');
		appendHtml(e,html, 'prepend');
		for (var i=0; i < listeners.length; i++) {
			addListener(el(listeners[i]), 'click', $FLCInspector)
		};
	}


	/**
	 * Console Class
	 */
	function Console()
	{
		var self = this;
		var inspectionPath = [];
		var inspectionIndex = 0;

		extend(self, {
			objects:[],
			firebug:false,
			open: function() {openCloseConsole(1);},
			close: function () {openCloseConsole(-1);},
			show: function() {
				if (!consoleCreated){ create(); }
				el('flashLoggerConsole').style.display='block';
				centerMe();
			},
			hide: function () {
				if (!consoleCreated){ create(); }
				el('flashLoggerConsole').style.display='none';
			},
			toggle: function () {openCloseConsole();},
			info: function() { addRowToHistory('info', arguments); },
			warn: function() { addRowToHistory('warn', arguments); },
			log: function() { addRowToHistory('log', arguments); },
			error: function() { addRowToHistory('error', arguments); },
			clear: function() {
				self.objects = [];
				el('flcHistoryPanel').innerHTML = '<div></div>';
				el('flcInstanceList').innerHTML = initial_instance_list;
			},
			count: function() {
				var c = el('flcHistoryPanel').childNodes.length -1;
				self.log(c);
			},
			assert: function() {
				self.log('Method assert() not implemented.');
			},
			dir: function() {
				self.log('Method dir() not implemented.');
			},
			trace: function() {
				self.log('Method trace() not implemented.');
			},
			inspect: function(e) {
				var et = eventTarget(e);
				var id = et.id;
				while (id == '') {
					et = et.parentNode;
					id = et.id;
				}

				if (id.substr(0,10)=='flcObject_') {
					self.inspectionIndex = id.substr(10);
					self.inspectionPath = [];
				} else {
					var pos = 20; // length of flcInspectorObject +2 for the 2 _s
					pos += self.inspectionIndex.toString().length; // add the lenght of the inspection index as a string.
					self.inspectionPath.push(id.substr(pos)); // gives us just the object's parameter name
				}
				self.buildInspect();
			},
			inspectbc: function(e) {
				var id = eventTarget(e).id;
				if (id == 'inspectorBreadcrumbsRoot') {
					self.inspectionPath = [];
				} else {
	 				var crumbs = id.substr(21);
					self.inspectionPath = crumbs.substr(0,crumbs.length-1).split('][');
				}
				self.buildInspect();
			},
			buildInspect: function() {
				var inspecting = self.objects[self.inspectionIndex];
				var html = '<p class="inspectorBreadcrumbs"><a href="#" id="inspectorBack">Back</a>';
				var bc_listeners = [];
				if (self.inspectionPath.length == 0)
				{
					html += '<span>' + ((inspecting.push) ? 'Array' : 'Object') + '</span>';
				} else {
					bc_listeners.push('inspectorBreadcrumbsRoot');
					html += '<span id="inspectorBreadcrumbsRoot" class="inspectorBCClickable">' + ((inspecting.push) ? 'Array' : 'Object') + '</span>';

				}

				var bc_id = 'inspectorBreadcrumbs';

				for (var i=0;i<self.inspectionPath.length;i++)
				{
					prev_inspecting = inspecting;
					inspecting = inspecting[self.inspectionPath[i]];
					if (i==self.inspectionPath.length-1)
					{
						if (typeof prev_inspecting == 'object' && prev_inspecting.push) {
							html += '[<span>' + self.inspectionPath[i] + '</span>]';
						} else {
							html += '.<span>' + self.inspectionPath[i] + '</span>';
						}
					} else {
						bc_id += '[' + self.inspectionPath[i] + ']'
						if (typeof prev_inspecting == 'object' && prev_inspecting.push) {
							html += '[<span id="' + bc_id + '" class="inspectorBCClickable">' + self.inspectionPath[i] + '</span>]';
						} else {
							html += '.<span id="' + bc_id + '" class="inspectorBCClickable">' + self.inspectionPath[i] + '</span>';
						}
						bc_listeners.push(bc_id);
					}
				}
				html += '</p>';


				var listeners = [];
				each(inspecting,function(k,v) {
					html += '<p class="inspectorrow"><span class="flcInspectorObjectKey">' + k + '</span>';
					var vt = typeof v;
					switch (vt) {
						case 'function':
							html += '<span class="flcInspectorItem_function">function</span>';
							break;
						case 'object':
							var objId = 'flcInspectorObject_' + self.inspectionIndex + '_' + k;
							listeners.push(objId);
							if (v!= null && v.push != null) {
								html += '<span class="flcInspectorItem_array" id="' +objId+ '">array (' + v.length + ' items)'
								var obj_items = [];
								var more_count = 0;
								each(v,function(k,v){
									if (obj_items.length > 4) { more_count++; return; }
									var ss ='';
									var vt = typeof v;
									switch (vt) {
										case 'function':
											ss += vt;
											break;
										case 'object' :
											if (v!= null && v.push != null) {
												ss += 'array'
											} else {
												ss += 'object'
											}
											break;
										case 'string' :
											ss += '"' + v + '"';
											break;
										case 'boolean': case 'number':
											ss += v;
											break;
										default:
											ss += 'unknown type (' + vt + ')';
									}
									obj_items.push(ss);
								});
								html += '<em> - ([' + obj_items.join(', ');
								if (more_count > 0) html += ', (' + more_count + 'more)';
								html += '])</em></span>';
							} else {
								html += '<span class="flcInspectorItem_object" id="' + objId + '">object'
								var obj_items = [];
								var more_count = 0;
								each(v,function(k,v){
									if (obj_items.length > 4) { more_count++; return; }
									var ss = k + ':';
									var vt = typeof v;
									switch (vt) {
										case 'function':
											ss += vt;
											break;
										case 'object' :
											if (v!= null && v.push != null) {
												ss += 'array'
											} else {
												ss += 'object'
											}
											break;
										case 'string' :
											ss += '"' + v + '"';
											break;
										case 'boolean': case 'number':
											ss += v;
											break;
										default:
											ss += 'unknown type (' + vt + ')';
									}
									obj_items.push(ss);
								});
								html += '<em> - ({' + obj_items.join(', ');
								if (more_count > 0) html += ', (' + more_count + 'more)';
								html += '})</em></span>';
							}
							break;
						case 'string':
							html += '<span class="flcInspectorItem_' + vt + '">"' + v + '"</span>'
							break;
						case 'number':
						case 'boolean':
							html += '<span class="flcInspectorItem_' + vt + '">' + v + '</span>';
							break;
						case 'undefined' :
							html += '<span class="flcInspectorItem_undefined">undefined</span>';
							break;
						default:
							html += '<span class="flcInspectorItem_unknown">unknown type: ' + vt + '</span>';
					}
					html += '</p>';
				});
				el('flcHistoryPanel').style.display = 'none';
				var inspector = el('flcInspectorPanel');
				inspector.style.display = 'block';
				inspector.innerHTML = '<div></div>';
				appendHtml(inspector,html);
				for (var i in listeners)
				{
					addListener(el(listeners[i]), 'click', $FLCInspector);
				}
				for (var i in bc_listeners)
				{
					addListener(el(bc_listeners[i]), 'click', $FLCBCInspector);
				}
				addListener(el('inspectorBack'), 'click', function(e){
					e = window.event || e;
					el('flcInspectorPanel').style.display = 'none';
					el('flcHistoryPanel').style.display = 'block';
					if (e.preventDefault) e.preventDefault();
					else return false;

				})
			},
			addObject: function(o) {
				return self.objects.push(o)-1;
			}
		});
		self.debug = self.log;
	}

	window.FlashLoggerConsole = window.$FLC = new Console;
})();

if (typeof console == "undefined" || typeof console.firebug == "undefined") {
	try {
		console = window.console = window.$FL('console');
	} catch(e) {}
}

function $FLCInspector(e)
{
	e = window.event || e;
	$FLC.inspect(e);
	if (e.preventDefault) e.preventDefault();
	else return false;
}
function $FLCBCInspector(e)
{
	e = window.event || e;
	$FLC.inspectbc(e);
	if (e.preventDefault) e.preventDefault();
	else return false;
}